﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Threading.Tasks;
using System.Globalization;
using ArgusPCLib.Collections.Generic;

namespace ArgusPCLib.Mathematics.Reflection
{
	public static class OperatorReflector
	{
		//static List<Type> registeredOperators;
		//static Task registerOperatorsTask;

		//static OperatorReflector()
		//{
		//	OperatorReflector.registeredOperators = new List<Type>();
		//	OperatorReflector.RegisterOperatorsInAssemblyAsync(typeof(OperatorReflector).GetTypeInfo().Assembly);
		//}

		//public static Task RegisterOperatorsInAssemblyAsync(Assembly assembly)
		//{
		//	if (assembly == null)
		//		throw new ArgumentNullException();

		//	Action taskAction = () =>
		//		{
		//			TypeInfo iOperatorType = typeof(IOperator).GetTypeInfo();
		//			foreach (TypeInfo type in assembly.DefinedTypes)
		//			{
		//				if (type.IsAbstract || type.IsInterface || !iOperatorType.IsAssignableFrom(type))
		//					continue;

		//				bool checkContains = true;
		//				lock(OperatorReflector.registeredOperators)
		//				{
		//					if (checkContains)
		//					{
		//						if (OperatorReflector.registeredOperators.Contains(type))
		//							return;
		//						checkContains = false;
		//					}
		//					OperatorReflector.registeredOperators.Add(type);
		//				}
		//			}
		//		};

		//	if (OperatorReflector.registerOperatorsTask == null)
		//		OperatorReflector.registerOperatorsTask = Task.Factory.StartNew(taskAction);
		//	else
		//		OperatorReflector.registerOperatorsTask = OperatorReflector.registerOperatorsTask.ContinueWith((task) => { taskAction(); });

		//	return OperatorReflector.registerOperatorsTask.ContinueWith((task) => { });
		//}

		//public static Type[] GetRegisteredOperatorTypes()
		//{
		//	if (OperatorReflector.registerOperatorsTask != null)
		//		OperatorReflector.registerOperatorsTask.Wait();
		//	lock(OperatorReflector.registeredOperators)
		//	{
		//		return OperatorReflector.registeredOperators.ToArray();
		//	}
		//}

		//public static IOperator[] GetRegisteredOperators()
		//{
		//	if (OperatorReflector.registerOperatorsTask != null)
		//		OperatorReflector.registerOperatorsTask.Wait();
		//	lock (OperatorReflector.registeredOperators)
		//	{
		//		IOperator[] RetVal = new IOperator[OperatorReflector.registeredOperators.Count];
		//		for (int i = 0; i < OperatorReflector.registeredOperators.Count; i++)
		//			RetVal[i] = OperatorReflector.GetInstance(OperatorReflector.registeredOperators[i]);
		//		return RetVal;
		//	}
		//}

		//public static Task<IOperator[]> GetRegisteredOperatorsAsync()
		//{
		//	return Task.Factory.StartNew<IOperator[]>(() =>
		//		{
		//			return OperatorReflector.GetRegisteredOperators();
		//		});
		//}

		//public static IOperator[] GetOperators(Type operandType)
		//{
		//	if (!operandType.Is(typeof(IOperand),false, false))
		//		throw new ArgumentException(string.Format(Resources.Exceptions.InterfaceImplementationRequired,
		//			typeof(IOperand)));

		//	if (OperatorReflector.registerOperatorsTask != null)
		//		OperatorReflector.registerOperatorsTask.Wait();

		//	List<IOperator> operators = new List<IOperator>();
		//	lock (OperatorReflector.registeredOperators)
		//	{
		//		foreach (Type operatorType in OperatorReflector.registeredOperators)
		//		{
		//			IOperator op = OperatorReflector.GetInstance(operandType);
		//			Type[][] operandTypes = op.GetSupportedParameterTypes();
		//			foreach (Type[] iOperandTypes in operandTypes)
		//			{
		//				bool _break = false;
		//				foreach (Type paramType in iOperandTypes)
		//				{
		//					if (operandType.Is(paramType))
		//					{
		//						operators.Add(op);
		//						_break = true;
		//						break;
		//					}
		//				}
		//				if (_break)
		//					break;
		//			}
		//		}
		//	}
		//	return operators.ToArray();
		//}

		//public static Task<IOperator[]> GetOperatorsAsync(Type operandType)
		//{
		//	if (!operandType.Is(typeof(IOperand)))
		//		throw new ArgumentException(string.Format(Resources.Exceptions.InterfaceImplementationRequired,
		//			typeof(IOperand)));

		//	return Task.Factory.StartNew<IOperator[]>(() =>
		//		{
		//			return OperatorReflector.GetOperators(operandType);
		//		});
		//}

		public static class MethodNames
		{
			public const string GetInstance = "GetInstance";
		}

		static MethodInfo GetGetInstanceMethodInfo(Type operatorType)
		{
			MethodInfo mi = operatorType.GetRuntimeMethod(MethodNames.GetInstance, new Type[] { }, false, true);
			if (mi == null || !mi.IsStatic || mi.ReturnType != operatorType)
				throw new NotImplementedException(string.Format(Resources.Exceptions.StaticMethodMissing,
					operatorType, operatorType, MethodNames.GetInstance, string.Empty));
			return mi;
		}

		public static Func<IOperator> GetGetInstanceHandler(Type operatorType)
		{
			MethodInfo mi = OperatorReflector.GetGetInstanceMethodInfo(operatorType);
			return (Func<IOperator>)mi.CreateDelegate(typeof(Func<IOperator>));
		}

		public static Func<T> GetGetInstanceHandler<T>() where T : IOperator
		{
			MethodInfo mi = OperatorReflector.GetGetInstanceMethodInfo(typeof(T));
			return (Func<T>)mi.CreateDelegate(typeof(Func<T>));
		}

		public static IOperator GetInstance(Type operatorType)
		{
			return OperatorReflector.GetGetInstanceHandler(operatorType)();
		}
	}

	public static class OperatorReflector<T> where T : IOperator
	{
		static Func<T> getInstanceHandler;

		static OperatorReflector()
		{
			OperatorReflector<T>.getInstanceHandler = OperatorReflector.GetGetInstanceHandler<T>();
			//OperatorReflector.RegisterOperatorsInAssemblyAsync(typeof(T).Assembly);
		}

		public static T GetInstance() { return OperatorReflector<T>.getInstanceHandler(); }
	}
}
